#ifndef __YLIB_TOKEN_BUCKET_H
#define __YLIB_TOKEN_BUCKET_H

#include <sys/types.h>

#include "ylock.h"


#define USECONDS_PER_SEC (1000000)

typedef enum {
        TOKEN_BUCKET_POLICY_A = 0,
        TOKEN_BUCKET_POLICY_B = 1,
} token_bucket_policy_t;

typedef struct {
        suseconds_t t;
        uint64_t consume;
} tb_sample_t;

typedef struct {
        char name[256];
        int inited;

        int64_t rate;
        int64_t capacity;
        int64_t burst_max;
        double water;

        suseconds_t t1;
} leaky_bucket_t;

typedef struct {
        char name[256];
        int inited;

        int priv;
        sy_rwlock_t lock;

        double rate;
        double capacity;
        int64_t burst_max;
        double tokens;

        suseconds_t t1;   ///上次token更新时间
        suseconds_t t2;   ///上次消耗token时间

        int include_leaky;
        leaky_bucket_t lb;

} token_bucket_t;

typedef struct {
        long avg;      // fill rate
        long burst_max;
        int burst_time;

        uint64_t rate_expire; //usec
        uint64_t level_expire;

        long capacity; /* max * time */
        long level;    // tokens
        long rate;     // current value
} level_bucket_t;

#define BUCKET_AVG_UPDATE_COUNT (60)  //secend
#define BUCKET_HOUR_UPDATE_COUNT (24)  //secend

typedef struct {
        sy_spinlock_t lock;
        time_t begin;
        time_t last;
        int cur;
        uint64_t intokens[BUCKET_AVG_UPDATE_COUNT];
        uint64_t outtokens[BUCKET_AVG_UPDATE_COUNT];

        time_t beginhour;
        time_t lasthour;
        int curhour;
        uint64_t intokens1hour[BUCKET_HOUR_UPDATE_COUNT];
        uint64_t outtokens1hour[BUCKET_HOUR_UPDATE_COUNT];

        level_bucket_t bucket;
} dynamic_bucket_t;

/* token bucket */
int token_bucket_init(token_bucket_t *bucket, const char *name, uint64_t capacity, uint64_t rate, uint64_t burst_max,
                      int private, int leaky);
void token_bucket_destroy(token_bucket_t *bucket);

int token_bucket_set(token_bucket_t *bucket, const char *name, uint64_t capacity, uint64_t rate, uint64_t burst_max,
                     int private, int leaky);

int token_bucket_consume(token_bucket_t *bucket, uint64_t n, int *is_ready, uint64_t *delay);
int token_bucket_consume_loop(token_bucket_t *tb, uint64_t n, int us, int retry);

int token_bucket_inc(token_bucket_t *bucket, uint64_t n);

// leaky bucket

int leaky_bucket_init(leaky_bucket_t *lb, uint64_t capacity, uint64_t rate);
void leaky_bucket_destroy(leaky_bucket_t *lb);

int leaky_bucket_set(leaky_bucket_t *lb, uint64_t capacity, uint64_t rate);
int leaky_bucket_take(leaky_bucket_t *lb, suseconds_t now, int *is_ready, uint64_t *delay);

/* level bucket */
#define BUCKET_LEVEL_UPDATE_INTERVAL USEC_PER_SEC  //usecend

int level_bucket_request(level_bucket_t *bucket, suseconds_t *delay, long size);
void level_bucket_init(level_bucket_t *bucket, int avg, int burst_max, int burst_time);
void level_bucket_set(level_bucket_t *bucket, int avg, int burst_max, int burst_time);

/* dynamic bucket */
/*
 * dynamic adjust avg base on level bucket
 */
#define BUCKET_AVG_UPDATE_INTERVAL (USEC_PER_SEC * BUCKET_AVG_UPDATE_COUNT)  //usecend

void dynamic_bucket_init(dynamic_bucket_t *bucket, long avg);
int dynamic_bucket_update(dynamic_bucket_t *bucket, long intokens, long outtokens);
int dynamic_bucket_request(dynamic_bucket_t *bucket, suseconds_t *delay, long size, int empty, int keep);
int dynamic_bucket_get_outtokens(dynamic_bucket_t *bucket, uint64_t *_avg, uint64_t *_max, uint64_t *_min);
int dynamic_bucket_get_outtokens_hour(dynamic_bucket_t *bucket, uint64_t *_avg, uint64_t *_max, uint64_t *_min);

#endif
